package afcp.alumni.util;

import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import afcp.alumni.model.Editable;

public class CopyFieldProcessor<T> {
	
	private static final Log logger = LogFactory.getLog(CopyFieldProcessor.class);
	
	private static final Class<Editable> editableClass = Editable.class;
	
	private final Class<T> entityClass;
	protected String entityClassName;
	
	List<Field> fields;
	private static class Field {
		public String field;
		public Method getter;
		public Method setter;
		public Field(String field, Method getter, Method setter) {
			this.field = field;
			this.getter = getter;
			this.setter = setter;
		}
	}
	
	public CopyFieldProcessor(Class<T> entityClass) {
		this.entityClass = entityClass;
		entityClassName = entityClass.getSimpleName();
		fields = initFields();
	}
	
	private List<Field> initFields() {

		List<Field> result = new LinkedList<>();

		for (java.lang.reflect.Field field : ReflectUtil.getFieldsWithAnnotation(
				entityClass, editableClass)) {
			String name = field.getName();
			try {
				Method[] getterSetter = ReflectUtil.findGetterSetter(entityClass,
						name);
				result.add(new Field(name, getterSetter[0], getterSetter[1]));
			} catch (NoSuchFieldException | NoSuchMethodException
					| IntrospectionException e) {
				logger.error("Could not find getter/setter methods for field "
						+ name, e);
			}
		}

		return result;
	}
	
	public void copyEditableFields(T source, T dest) {

		for (Field field : fields) {
			try {
				Object value = field.getter.invoke(source);
				field.setter.invoke(dest, value);
			} catch (IllegalAccessException | IllegalArgumentException
					| InvocationTargetException e) {
				logger.error("Could not execute getter of field " + field.field
						+ " for entity " + source + " (method: " + field.getter
						+ ") or setter of same field for entity " + dest
						+ " (method: " + field.setter + ").", e);
			}
		}
	}
	
}